package com.haixiaoke.saas.common.weChat.util;

import com.haixiaoke.saas.common.utils.sign.Md5Utils;
import com.haixiaoke.saas.common.weChat.domain.ParseParam;
import org.apache.commons.lang3.StringUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.*;


/**
 * 微信工具类
 * 
 * @author JsonChen
 */
public class WeChatUtils {
	private static final Logger log = LoggerFactory.getLogger(WeChatUtils.class);

	private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
	private static final Random RANDOM = new SecureRandom();

	public static final String FAIL = "FAIL";
	public static final String SUCCESS = "SUCCESS";

	/**
	 * 获取随机字符串 Nonce Str
	 *
	 * @return String 随机字符串
	 */
	public static String generateNonceStr() {
		char[] nonceChars = new char[32];
		for (int index = 0; index < nonceChars.length; ++index) {
			nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
		}
		return new String(nonceChars);
	}

	/**
	 * 统一下单签名算法
	 *
	 * @param params 要参与签名的数据对象
	 * @param mchKey 商户平台设置的密钥key
	 * @return 签名
	 */
	public static String getSign(SortedMap<String, Object> params, String mchKey) {
		StringBuffer sb = new StringBuffer();
		Set<Map.Entry<String, Object>> es = params.entrySet();
		Iterator<Map.Entry<String, Object>> it = es.iterator();
		while (it.hasNext()) {
			Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
			String k = (String) entry.getKey();
			Object v = entry.getValue();
			if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		// 商户平台密钥key必须放在最后面
		sb.append("key=" + mchKey);
		return Md5Utils.hash(sb.toString()).toUpperCase();
	}

	/**
	 * 获取本机的IP
	 * @return 终端IP
	 * @throws UnknownHostException
	 */
	public static String getIp() throws UnknownHostException {
		//InetAddress.getLocalHost()方法获取本地主机的InetAddress对象，然后使用getHostAddress()方法获取IP地址
		InetAddress inetAddress = InetAddress.getLocalHost();
		return inetAddress.getHostAddress();
	}

	/**
	 * 签名验证
	 *
	 * @param mchKey      商户平台设置的密钥key
	 * @return
	 */
	public static boolean signCheck(ParseParam parseParam, String mchKey) {
		// 微信返回数据中的签名
		String wxSign = parseParam.getSign();
		if (StringUtils.isBlank(wxSign)) {
			return false;
		}
		// 重新签名
		SortedMap<String, Object> paramMap = new TreeMap<String, Object>();
		paramMap.put("total_fee",parseParam.getTotalFee());
		paramMap.put("transaction_id",parseParam.getTransactionId());
		paramMap.put("out_trade_no",parseParam.getOutTradeNo());
		paramMap.put("attach",parseParam.getAttach());
		paramMap.put("return_code",parseParam.getReturnCode());
		paramMap.put("return_msg",parseParam.getReturnMsg());
		paramMap.put("result_code",parseParam.getResultCode());
		paramMap.put("err_code",parseParam.getErrCode());
		paramMap.put("err_code_des",parseParam.getErrCodeDes());
		paramMap.put("mch_id",parseParam.getMchId());
		paramMap.put("nonce_str",parseParam.getNonceStr());
		paramMap.put("time_end",parseParam.getTimeEnd());
		paramMap.put("sign",parseParam.getSign());
		paramMap.put("appid",parseParam.getAppId());
		paramMap.put("device_info",parseParam.getDeviceInfo());
		paramMap.put("sign_type",parseParam.getSignType());
		paramMap.put("openid",parseParam.getOpenId());
		paramMap.put("is_subscribe",parseParam.getIsSubscribe());
		paramMap.put("trade_type",parseParam.getTradeType());
		paramMap.put("bank_type",parseParam.getBankType());
		paramMap.put("settlement_total_fee",parseParam.getSettlementTotalFee());
		paramMap.put("fee_type",parseParam.getFeeType());
		paramMap.put("cash_fee",parseParam.getCashFee());
		paramMap.put("cash_fee_type",parseParam.getCashFeeType());
		paramMap.put("coupon_fee",parseParam.getCouponFee());
		paramMap.put("coupon_count",parseParam.getCouponCount());
		paramMap.put("coupon_type_$n",parseParam.getCouponType());
		paramMap.put("coupon_id_$n",parseParam.getCouponId());
		paramMap.put("coupon_fee_$n",parseParam.getCouponFeeTwo());
		String sign = getSign(paramMap, mchKey);

		// 二者签名对比验证
		return wxSign.equals(sign);
	}

	/**
	 * 将xml格式字符串格式化对应Java对象
	 * @param xmlStr
	 */
	public static ParseParam parseParam(String xmlStr) throws JAXBException {
		JAXBContext context = JAXBContext.newInstance(ParseParam.class);
		Unmarshaller unmarshaller = context.createUnmarshaller();
        return (ParseParam) unmarshaller.unmarshal(new StringReader(xmlStr));
	}

	/**
	 * 解析xml,返回第一级元素键值对。如果第一级元素有子节点，则此节点的值是子节点的xml数据。
	 * @param strXml
	 * @return
	 * @throws JDOMException
	 * @throws IOException
	 */
	public static Map<String,String> doXMLParse(String strXml) throws JDOMException, IOException {
		strXml = strXml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

		if(null == strXml || "".equals(strXml)) {
			return null;
		}
		Map m = new HashMap();
		InputStream in = new ByteArrayInputStream(strXml.getBytes("UTF-8"));
		SAXBuilder builder = new SAXBuilder();
		Document doc = builder.build(in);
		Element root = doc.getRootElement();
		List list = root.getChildren();
		Iterator it = list.iterator();
		while(it.hasNext()) {
			Element e = (Element) it.next();
			String k = e.getName();
			String v = "";
			List children = e.getChildren();
			if(children.isEmpty()) {
				v = e.getTextNormalize();
			} else {
				v = getChildrenText(children);
			}
			m.put(k, v);
		}
		//关闭流
		in.close();

		return m;
	}

	/**
	 * 获取子结点的xml
	 * @param children
	 * @return String
	 */
	public static String getChildrenText(List children) {
		StringBuffer sb = new StringBuffer();
		if(!children.isEmpty()) {
			Iterator it = children.iterator();
			while(it.hasNext()) {
				Element e = (Element) it.next();
				String name = e.getName();
				String value = e.getTextNormalize();
				List list = e.getChildren();
				sb.append("<" + name + ">");
				if(!list.isEmpty()) {
					sb.append(getChildrenText(list));
				}
				sb.append(value);
				sb.append("</" + name + ">");
			}
		}
		return sb.toString();
	}

}
